50  数据框可视化

本章定位(复习与补充训练层):本章对应正课第18章《Matplotlib库基础》(章节 18)与第19章《Seaborn库基础》(章节 19),图形选型与多子图组织另可参考第20章《常用图形绘制》(章节 20),并与第54—58章《绘制曲线图》(章节 55)至《绘制饼图》(章节 59)互为补充训练(本章为综合版,五章为分项版),用于复习、补缺与额外练习。建议先不看讲解,直接尝试下方平台任务,再对照解析补弱项。本章不属于必修主线。先做本章『动手与思考』第 1 题与平台任务自测,通过即可跳过本章。

50.1 引言 数据可视化的价值

数据可视化把抽象数字转译为图形语言,是发现模式、沟通结论、支持决策的基础工具。本章按”基础图表—统计图表—散点回归—热力图”的顺序复习 Python 可视化工具链,并完成平台四个绘图任务。

50.2 本章学习目标

通过本章的复习与补充训练,你将能够:

  1. 按”建画布→切子图→绘制→装饰→调整布局→保存/显示”的流程,用 plt.plot()/plt.bar()/plt.scatter() 绘制折线图、柱状图与散点图
  2. 配置 plt.rcParams['font.sans-serif'] 解决中文显示问题,并为图形补齐标题、坐标轴标签与网格
  3. sns.boxplot()sns.violinplot()sns.regplot()sns.heatmap() 呈现分布、回归关系与相关结构
  4. 用 DataFrame 的 .plot(kind='line'/'hist'/'box') 快速出图,并用 plt.savefig() 保存结果,独立完成平台四个任务

50.3 基础图表绘制

折线图和柱状图是金融分析中最常用的两种图表类型。

理论背景:可视化与数据理解

数据可视化(Data Visualization)是将抽象数据转换为图形表示的过程。在金融数据分析中,可视化扮演着不可替代的角色:

  1. 模式发现:人类视觉系统对模式和异常的识别能力远强于对数字表格的扫描能力
  2. 沟通工具:一张精心设计的图表往往比千言万语更能传达数据洞察
  3. 决策支持:可视化帮助决策者快速理解数据趋势,做出更明智的商业决策

本章的工具链由三个层次构成:Matplotlib、Seaborn与Pandas的集成形成了一个完整的Python可视化生态系统:

  • Matplotlib:底层绘图库,提供完整的控制能力
  • Seaborn:基于Matplotlib的高级接口,专为统计可视化设计
  • Pandas:.plot()方法提供快速绘图能力
列表 50.1
# =============================================================================
# 题目:绘制基础金融图表
# =============================================================================
# 本任务演示如何使用Matplotlib绘制折线图和柱状图

# ==================== 导入必要的库 ====================
import matplotlib.pyplot as plt  # Matplotlib的 pyplot 接口,提供类似 MATLAB 的绘图API
import pandas as pd                 # Pandas 数据分析库
import seaborn as sns               # Seaborn 统计可视化库

# ==================== 设置中文字体支持 ====================
# Matplotlib默认不支持中文显示,需要手动设置中文字体
# plt.rcParams是一个全局配置字典,用于控制绘图的各种属性
plt.rcParams['font.sans-serif'] = ['Source Han Serif SC', 'SimHei']  # 规范字体在前,SimHei 仅作后备  # 设置默认字体为黑体(SimHei)
# plt.rcParams['axes.unicode_minus'] = False     # 解决负号'-'显示为方块的问题
# 这两行设置确保图表中的中文文字和负号都能正确显示

# ==================== 准备示例数据 ====================
# 场景:模拟公司5个月的销售额和利润数据
sales = pd.DataFrame({
    '月份': ['1月', '2月', '3月', '4月', '5月'],        # 时间维度(X轴)
    '销售额': [120, 150, 180, 140, 200],             # 销售额数据(用于折线图和柱状图)
    '利润': [30, 40, 50, 35, 60]                      # 利润数据(用于柱状图)
})

# ==================== 创建图形画布 ====================
# plt.figure()创建一个新的图形窗口
# figsize=(12, 6)指定图形大小为12英寸宽×6英寸高
# 这个比例适合并排显示两个子图
plt.figure(figsize=(12, 6))

# ==================== 绘制第一个子图:折线图 ====================
# plt.subplot(1, 2, 1)创建一个1行2列的子图布局,并激活第1个子图
# 三个参数的含义:(行数, 列数, 当前子图编号)
plt.subplot(1, 2, 1)

# plt.plot()绘制折线图
# sales['月份']:X轴数据(月份)
# sales['销售额']:Y轴数据(销售额)
# marker='o':在每个数据点处绘制圆形标记
# linewidth=2:设置线条宽度为2(使折线更明显)
plt.plot(sales['月份'], sales['销售额'], marker='o', linewidth=2)

# 设置标题和标签
plt.title('销售额趋势', fontsize=14)              # 设置图表标题,字号14
plt.xlabel('月份', fontsize=12)                      # 设置X轴标签
plt.ylabel('销售额(万元)', fontsize=12)               # 设置Y轴标签
plt.grid(True, alpha=0.3)                            # 添加网格线,alpha=0.3设置透明度(使网格不明显)

# ==================== 绘制第二个子图:柱状图 ====================
# plt.subplot(1, 2, 2)激活第2个子图
plt.subplot(1, 2, 2)

# plt.bar()绘制柱状图
# sales['月份']:X轴数据(月份)
# sales['利润']:柱子高度(利润)
# color='steelblue':设置柱子颜色为钢蓝色
# alpha=0.7:设置透明度(0.7使柱子有轻微透明效果)
plt.bar(sales['月份'], sales['利润'], color='steelblue', alpha=0.7)

# 设置标题和标签
plt.title('利润对比', fontsize=14)                  # 设置图表标题
plt.xlabel('月份', fontsize=12)                        # 设置X轴标签
plt.ylabel('利润(万元)', fontsize=12)                 # 设置Y轴标签
plt.grid(axis='y', alpha=0.3)                         # 只显示Y轴网格线(水平网格线)

# ==================== 调整布局并显示图表 ====================
# plt.tight_layout()自动调整子图之间的间距,防止标签重叠
plt.tight_layout()
# plt.show()将图形显示出来
plt.show()

50.4 Seaborn高级统计图表

Seaborn在Matplotlib基础上提供了更高级的统计可视化功能,特别适合展示数据的分布特征和统计关系。

列表 50.2
# =============================================================================
# 题目:绘制Seaborn高级统计图表
# =============================================================================
# 本任务演示如何使用Seaborn绘制箱线图和小提琴图来展示数据分布

# ==================== 导入Seaborn库 ====================
import seaborn as sns  # Seaborn统计可视化库

# ==================== 创建图形画布 ====================
plt.figure(figsize=(12, 6))

# ==================== 绘制第一个子图:箱线图 ====================
# plt.subplot(1, 2, 1)创建1行2列布局,激活第1个子图
plt.subplot(1, 2, 1)

# sns.boxplot()绘制箱线图(Boxplot)
# 箱线图显示数据的五个统计量:最小值、第一四分位数、中位数、第三四分位数、最大值
# 同时可以显示异常值(超出1.5倍IQR的数据点)
# data参数:数据源DataFrame
# x参数:X轴变量(分类变量,这里是月份)
# y参数:Y轴变量(数值变量,这里是销售额)
sns.boxplot(data=sales, x='月份', y='销售额')

# 设置标题
plt.title('销售额箱线图', fontsize=14)
# 箱线图的解读:
#   - 箱体:包含中间50%的数据(Q1到Q3)
#   - 箱中的横线:中位数
#   - 上下须:数据的正常范围(非异常值范围)
#   - 圆点:异常值(如果有的话)

# ==================== 绘制第二个子图:小提琴图 ====================
# plt.subplot(1, 2, 2)激活第2个子图
plt.subplot(1, 2, 2)

# sns.violinplot()绘制小提琴图(Violin Plot)
# 小提琴图结合了箱线图和核密度估计图,能更直观地展示数据分布形状
# 参数含义与boxplot相同
sns.violinplot(data=sales, x='月份', y='销售额')

# 设置标题
plt.title('销售额小提琴图', fontsize=14)
# 小提琴图的解读:
#   - 小提琴的宽度表示该位置数据点的密度
#   - 较宽的部分表示数据更集中
#   - 内部的箱线图元素显示统计量

# ==================== 调整布局并显示图表 ====================
plt.tight_layout()  # 自动调整子图间距
plt.show()         # 显示图形

50.5 散点图与回归分析

散点图是探索两个数值变量之间关系的经典工具。在金融分析中,散点图广泛用于研究两个变量(如股票价格与成交量、风险与收益)之间的相关性。

列表 50.3
# =============================================================================
# 题目:绘制散点图并添加回归线
# =============================================================================
# 本任务演示如何绘制散点图并拟合线性回归线

# ==================== 导入NumPy库 ====================
import numpy as np  # NumPy数值计算库,用于生成随机数

# ==================== 创建模拟数据 ====================
# 场景:模拟两个相关的金融变量(如风险与收益)
# 生成100个随机数据点
# np.random.randn(100)生成100个标准正态分布的随机数
# * 10 + 50将数据缩放和移位,使其均值约为50,标准差约为10
np.random.seed(42)  # 固定随机种子,保证每次运行生成相同的散点
x = np.random.randn(100) * 10 + 50
# x表示自变量(如风险水平)

# 生成因变量y,与x正相关,但加入随机噪声
# x * 0.8:y与x的线性关系,斜率为0.8
# np.random.randn(100) * 5:添加随机噪声,标准差为5
# + 30:将数据移位,使均值约为30
y = x * 0.8 + np.random.randn(100) * 5 + 30
# y表示因变量(如收益),与x正相关但存在噪声

# ==================== 创建图形并绘制散点图 ====================
plt.figure(figsize=(10, 6))  # 创建10英寸宽×6英寸高的图形

# plt.scatter()绘制散点图
# x, y:X坐标和Y坐标数据
# alpha=0.6:设置透明度为0.6(使重叠的点更可见)
plt.scatter(x, y, alpha=0.6)

# ==================== 使用Seaborn添加回归线 ====================
# sns.regplot()绘制散点图并自动拟合线性回归线
# scatter_kws={'alpha':0.6}:将散点图的透明度设置为0.6(通过字典参数传递)
# Seaborn会自动:
#   1. 绘制散点图
#   2. 使用OLS(最小二乘法)拟合线性回归线
#   3. 绘制回归线和置信区间(阴影区域)
sns.regplot(x=x, y=y, scatter_kws={'alpha':0.6})

# ==================== 设置图形属性 ====================
plt.title('散点图与回归线', fontsize=14)
plt.xlabel('X变量', fontsize=12)
plt.ylabel('Y变量', fontsize=12)
plt.grid(True, alpha=0.3)    # 添加网格线,便于观察数据趋势
plt.tight_layout()            # 自动调整布局,防止标签被截断
plt.show()

# ==================== 图表解读 ====================
# 散点图显示了100个数据点的分布情况
# 回归线(红色直线)显示x和y的整体趋势
# 阴影区域表示回归线的置信区间(不确定性范围)
# 由于y = x * 0.8 + 噪声,回归线的斜率应该接近0.8

50.6 相关性热力图

热力图(Heatmap)是用颜色编码矩阵元素值的可视化方法。在金融分析中,热力图是展示相关性矩阵的最佳工具,可以一目了然地看出哪些变量之间存在强相关关系。它把”数值”直接映射到”颜色”这一视觉通道——这正是视觉编码原理的应用。

数学基础:视觉编码的原理

数据可视化的本质是将数据属性(数值、类别、时间)映射到视觉通道(位置、长度、颜色、形状)。有效的可视化应在保持可读性的前提下,让每个视觉元素都承载信息——面积、颜色、位置都应编码数据,而非装饰。

列表 50.4
# =============================================================================
# 题目:绘制相关性矩阵热力图
# =============================================================================
# 本任务演示如何计算相关系数矩阵并使用热力图可视化

# ==================== 准备示例数据 ====================
# 场景:模拟多个金融指标的数据,用于计算相关性
# 创建一个DataFrame,包含4个特征(A, B, C, D)和5个观测
data = pd.DataFrame({
    'A': [1, 2, 3, 4, 5],        # 特征A:递增序列
    'B': [5, 4, 3, 2, 1],        # 特征B:递减序列(与A负相关)
    'C': [2, 3, 4, 5, 6],        # 特征C:递增序列
    'D': [4, 5, 6, 7, 8]         # 特征D:递增序列
})

# ==================== 计算相关系数矩阵 ====================
# df.corr()方法计算所有列之间的Pearson相关系数
# Pearson相关系数衡量两个变量之间的线性相关程度
# 取值范围:[-1, 1]
#   - 1:完全正相关
#   - 0:无线性相关
#   - -1:完全负相关
corr = data.corr()
# 结果是一个4×4的矩阵(对称矩阵,对角线为1)

# ==================== 绘制热力图 ====================
plt.figure(figsize=(10, 8))  # 创建10×8英寸的图形

# sns.heatmap()绘制热力图
# corr:相关系数矩阵(数据源)
# annot=True:在每个格子中显示数值注释
# cmap='coolwarm':使用蓝-红配色方案(冷-暖色)
#   负相关显示为蓝色,正相关显示为红色
# center=0:设置颜色中心值为0,确保0值显示为中间色
# linewidths=0.5:格子之间的线宽
# cbar_kws={'label': '相关系数'}:颜色条的标签文本
sns.heatmap(
    corr,
    annot=True,
    cmap='coolwarm',
    center=0,
    linewidths=0.5,
    cbar_kws={'label': '相关系数'}
)

# 设置标题
plt.title('特征相关性热力图', fontsize=14)
plt.tight_layout()            # 调整布局
plt.show()

# ==================== 图表解读 ====================
# 热力图中的每个格子代表两个特征之间的相关系数
# 颜色越红:正相关越强(接近1)
# 颜色越蓝:负相关越强(接近-1)
# 颜色接近白色:相关性较弱(接近0)
# 对角线为1(自相关,总是完全相关)
#
# 从本例中可以看出:
#   - A与B:负相关(一个递增,一个递减)
#   - A与C、A与D、C与D等:正相关(都递增)

平台任务(平台原始代码)

以下代码与教学平台任务要求完全一致:

任务要求:任务一,读取 Sheet1 的换手率数据,把日期设为索引后绘制折线图并保存;任务二,读取 Sheet2 的交易金额数据,绘制直方图并保存;任务三,对 Sheet2 数据查看统计属性并绘制箱线图;任务四,读取 Sheet3 的开高低收数据,绘制 2×2 子图的走势图并保存。请将代码原样输入教学平台(注释除外),判定以平台为准。

列表 50.5: 平台原始代码 (1)-(4)
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
#任务一
import matplotlib.pyplot as plt
import pandas as pd  # 导入Pandas数据分析库

plt.rcParams["font.sans-serif"] = ["SimHei"]  # 设置Matplotlib全局参数
turnover = pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1726219804739.xlsx",sheet_name="Sheet1")  #导入外部换手率数据并且是Sheet1工作表
turnover["日期"] = pd.to_datetime(turnover["日期"] , format='%Y%m%d')  # 转换为日期时间格式
turnover.set_index("日期",inplace=True)  # 将日期列设为turnover数据框的索引
turnover.head() #显示前五行
turnover.tail() #显示后五行
# 绘制换手率的折线图(显示时间趋势)
turnover.plot(kind="line",figsize=(9,6),title="2024年1月至2024年8月上证50指数换手率",grid=True,fontsize=13)
plt.savefig("1.png")  # 保存图形至文件

#任务二
import matplotlib.pyplot as plt
import pandas as pd  # 导入Pandas数据分析库
plt.rcParams["font.sans-serif"] = ["SimHei"]  # 设置Matplotlib全局参数

# 从Excel文件读取数据存入volume
volume = pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1726219804739.xlsx",sheet_name="Sheet2",header=0)  

volume["日期"] = pd.to_datetime(volume["日期"] , format='%Y%m%d')  # 转换为日期时间格式
volume.set_index("日期",inplace=True)  # 将日期列设为volume数据框的索引
# 绘制交易金额的直方图(展示分布特征)
volume.plot(kind="hist",figsize=(9,6),title="2024年1月至2024年9月上证50指数交易金额",grid=True,fontsize=13)
plt.savefig("1.png")  # 保存图形至文件


#任务三
import matplotlib.pyplot as plt
import pandas as pd  # 导入Pandas数据分析库
plt.rcParams["font.sans-serif"] = ["SimHei"]  # 设置Matplotlib全局参数

# 从Excel文件读取数据存入volume
volume = pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1726219804739.xlsx",sheet_name="Sheet2",header=0) 
volume["日期"] = pd.to_datetime(volume["日期"] , format='%Y%m%d')  # 转换为日期时间格式
volume.set_index("日期",inplace=True)  # 将日期列设为volume数据框的索引
volume.describe() #查看数据属性
# 绘制交易金额的箱线图(展示分位数和异常值)
volume.plot(kind="box",figsize=(9,6),title="2024年1月至2024年9月上证50指数交易金额",grid=True,fontsize=13)
plt.savefig("1.png")  # 保存图形至文件

#任务四
import matplotlib.pyplot as plt
import pandas as pd  # 导入Pandas数据分析库
plt.rcParams["font.sans-serif"] = ["SimHei"]  # 设置Matplotlib全局参数

# 从Excel文件读取数据存入price
price = pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1726219804739.xlsx",sheet_name="Sheet3")
price ["日期"] = pd.to_datetime(price["日期"] , format='%Y%m%d')  # 转换为日期时间格式
price.set_index("日期",inplace=True)  # 将日期列设为price数据框的索引
price.head()  # 查看price前5行数据
price.tail()  # 查看price后5行数据
price.plot(kind="line",subplots=True,sharex=True,sharey=True,layout=(2,2),figsize=(10,8),title="2024年1月至2024年9月上证50指数走势图",grid=True,fontsize=13) #2018年1月至2019年6月上证50指数走势图
plt.savefig("1.png")  # 保存图形至文件

预期输出(OSS 直链本机实测;四个任务均为绘图任务,head()/tail()/describe() 未加 print,脚本方式运行控制台无输出;具体以平台运行结果为准):

  • 任务一:生成 1.png,单序列折线图;数据为 162 个交易日(2024-01-02 至 2024-08-30)的上证50成分股成交量,首值 31375477、末值 63786046,区间最高 64784099(2024-02-07)、最低 22049930(2024-08-14)——2 月初与 8 月末各有一个尖峰,中枢约 3.7×10^7,纵轴以 1e7 计
  • 任务二:直方图,默认 10 根柱,横轴约 323—963(亿元);最高柱位于 [579, 643) 区间(51 个观测),整体右偏,右尾一直拖到 900 亿元以上
  • 任务三:单箱箱线图,五数概括为最小 323.5、Q1 519.4、中位数 598.6、Q3 666.7、最大 963.5(亿元);上须之上出现 4 个高端离群点,对应 2024-02-07(954.3)、2024-03-12(933.4)、2024-04-29(920.6)、2024-05-06(963.5)
  • 任务四:2×2 子图,四个子图依次为开盘价、最高价、最低价、收盘价;四个序列共用纵轴(约 2164—2545),均自 1 月初的 2292—2317 起步,于 2024-05-20 前后同步见顶(2530—2545),8 月 30 日收于 2311—2355,呈”先扬后抑”形态
  • 本机未安装 SimHei 字体,图上中文显示为方框并伴随 findfont 警告(环境问题而非代码错误,教学平台已配置中文字体,以平台渲染为准)

50.7 本章小结

要点:

  • 绘图固定流程:准备数据 → plt.figure(figsize=...) 建画布 → plt.subplot(行,列,编号) 切子图 → 调用绘图函数 → 补标题、轴标签、网格 → plt.tight_layout() 调间距 → show() 显示或 savefig() 保存
  • 中文显示靠 plt.rcParams['font.sans-serif'] = ['SimHei'];散点图配 alpha 透明度可缓解数据点重叠
  • 图形跟随目的:趋势用折线图、分类对比用柱状图、分布用直方图/箱线图/小提琴图、两变量关系用散点图加回归线、相关矩阵用热力图
  • DataFrame 的 .plot(kind=...) 直接以行索引为 x 轴、数值列为 y 轴,subplotslayoutsharex/sharey 控制多子图组织
  • 箱线图读五个统计量(最小值、Q1、中位数、Q3、最大值)与离群点;小提琴图在此基础上用宽度表达密度

易错点:

  • savefig 必须在 show 之前调用;show 之后画布被清空,再保存得到的是空白图
  • plt.subplot(1, 2, 1) 的三个参数是行数、列数、当前子图编号,编号从 1 开始,与 Python 索引从 0 开始的习惯相反
  • 本机没有 SimHei 字体时中文显示为方框并伴随 findfont 警告,属环境问题而非代码错误;平台已配置中文字体,以平台渲染为准
  • sns.heatmap 的输入应是对相关系数矩阵(先 df.corr()),直接喂原始 DataFrame 会把每个数值都当成”颜色强度”
  • sns.boxplot/sns.violinplotx 应为分类变量、y 为数值变量,传反后图形失去统计含义

50.8 动手与思考

以下练习每题附参考答案(默认折叠)。请先独立完成并写下你的判断,再点开对照,最后上机验证。

  1. 输出预测:热力图的颜色由相关系数矩阵决定。不运行代码,先写出下面代码的输出(一个 3×3 矩阵),再上机检验你的判断。

    import pandas as pd
    
    data = pd.DataFrame({'A': [1, 2, 3], 'B': [3, 2, 1], 'C': [1, 2, 3]})
    print(data.corr().round(2))

    参考答案(先写下你的预测再点开)

    解题思路:A 与 C 的取值完全相同,相关系数为 1;B 恰是 A 的反向序列(3、2、1 对 1、2、3),相关系数为 −1。于是对角线全为 1,其余位置按”A 与 C 同向、B 与 A/C 反向”填充:主对角线为 1.0,A−B、B−C 为 −1.0,A−C 为 1.0,矩阵关于对角线对称。

    # 验证脚本:同向与反向序列的相关系数矩阵
    import pandas as pd  # 导入pandas库
    data = pd.DataFrame({'A': [1, 2, 3], 'B': [3, 2, 1], 'C': [1, 2, 3]})  # B为A的反向序列
    print(data.corr().round(2))  # 相关系数矩阵保留2位小数

    预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):

         A    B    C
    A  1.0 -1.0  1.0
    B -1.0  1.0 -1.0
    C  1.0 -1.0  1.0

    回扣主线sns.heatmap 的输入应先经 df.corr() 得到相关系数矩阵,见第 章节 19 章与本章”相关性热力图”一节。

  2. 自测回忆:不看正文,为以下分析目的各选一种图形并说明理由:展示 8 个月指数换手率的时间趋势、比较多只基金的净值分布、考察风险与收益的关系、呈现多只股票收益率之间的相关结构。

    参考答案(点开前请先独立完成)

    解题思路:图形跟随目的。第一,8 个月换手率的时间趋势选折线图——时间顺序由左至右排列,线段连接突出升降走势。第二,比较多只基金的净值分布选箱线图(或小提琴图)——多个箱体沿分类轴并排,五数概括(最小值、Q1、中位数、Q3、最大值)与离群点一图可比;直方图适合单一变量的细致分布,多组并排会拥挤。第三,风险与收益的关系选散点图(可叠加回归线)——每个基金是一点,横风险纵收益,方向与离散程度直观可见。第四,多只股票收益率之间的相关结构选热力图——相关系数矩阵的每个格子按数值着色,多变量两两关系一图尽览。

    回扣主线:“趋势用折线图、分布用箱线图/小提琴图、两变量关系用散点图、相关矩阵用热力图”的选型要点见第 章节 20 章与本章”要点”第 3 条。

  3. 变式任务(平台任务同型改造):把平台任务一的折线图改为柱状图,再为平台任务三的箱线图叠加一层小提琴图(sns.violinplot),对比两种图形对分布信息的呈现差异。

    参考答案(点开前请先独立完成)

    解题思路:改造思路——任务一把 kind='line' 改为 kind='bar' 即可,其余语句不动;任务三在箱线图之上先画 sns.violinplot(inner=None) 再叠加箱体。判读要点:162 个交易日的成交量画成柱状图后每根柱极窄、相邻柱几乎粘连,时间趋势反而不如折线图易读——柱状图更适合少量类别的比较而非高频时序;箱线图叠加小提琴图后,箱体给出五数概括与 4 个高端离群点(约 930—980 亿元),小提琴的宽度则补充了分布形状(主体集中在 600 亿元附近、上尾拖长),两者互补。以下代码在 OSS 直链数据上实跑(拉取日期 2026-08-27;若链接失效,可到教学平台运行平台任务原码,或用任一单列数值序列演练,代码不变)。

    # 变式脚本:柱状图变体与小提琴图叠加箱线图
    import matplotlib.pyplot as plt  # 导入绘图库
    import pandas as pd  # 导入pandas库
    import seaborn as sns  # 导入seaborn库
    turnover = pd.read_excel('https://huoran.oss-cn-shenzhen.aliyuncs.com/1726219804739.xlsx', sheet_name='Sheet1')  # 读取换手率数据
    turnover['日期'] = pd.to_datetime(turnover['日期'], format='%Y%m%d')  # 日期列转日期类型
    turnover.set_index('日期', inplace=True)  # 日期设为行索引
    turnover.iloc[::4].plot(kind='bar', figsize=(11, 5), grid=True)  # 柱状图变体(每4日取1根柱以免过密)
    plt.savefig('bar_variant.png')  # 保存图形
    volume = pd.read_excel('https://huoran.oss-cn-shenzhen.aliyuncs.com/1726219804739.xlsx', sheet_name='Sheet2', header=0)  # 读取交易金额数据
    volume['日期'] = pd.to_datetime(volume['日期'], format='%Y%m%d')  # 日期列转日期类型
    volume.set_index('日期', inplace=True)  # 日期设为行索引
    fig, ax = plt.subplots(figsize=(7, 5))  # 新建画布
    sns.violinplot(y=volume.iloc[:, 0], ax=ax, color='lightgray', inner=None)  # 底层小提琴图只画轮廓
    ax.boxplot(volume.iloc[:, 0], widths=0.15)  # 上层叠加窄箱线图
    plt.savefig('violin_box.png')  # 保存图形

    预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):代码无控制台输出、正常生成两个图片文件;柱状图 162 根柱过密(示例中每 4 日取 1 根柱缓解);叠加图中小提琴主体最宽处约在 600 亿元、上尾拖至 930—980 亿元处箱线图出现 4 个离群点,与本章平台任务三记录的五数概括一致。

    注意:以上为变式代码;列表 50.5 的平台任务仍须按原始代码原样输入教学平台。

    回扣主线:箱线图五数概括与小提琴图密度宽度的分工见第 章节 19 章与本章”要点”第 5 条;高频时序不宜用柱状图的判读见第 章节 20 章。

  4. 变式任务:把本章热力图示例的 cmap='coolwarm' 分别换为 'viridis''Greens',并去掉 center=0,观察配色改变后”正相关/负相关”的辨识难度如何变化,由此说明选择发散型配色的理由。

    参考答案(点开前请先独立完成)

    解题思路:结论——coolwarm 是发散型配色,配合 center=0 时白色恰好对准相关系数 0,红/蓝两个色相分别编码正/负,深浅编码强弱,方向与强度一眼可读;换成 viridis(多色顺序型)后正负相关只能靠”颜色不同”记忆对照,0 附近的中性参照消失;换成 Greens(单色顺序型)后所有取值只是同一色相的深浅,负相关与弱正相关可能呈现相近的浅色,方向信息完全丢失,只能依赖格内数字。这就是选择发散型配色的理由:相关系数的符号是有语义的分界,配色必须给 0 一个视觉上的”锚点”。

    # 变式脚本:三种配色的相关系数热力图对照
    import matplotlib.pyplot as plt  # 导入绘图库
    import pandas as pd  # 导入pandas库
    import seaborn as sns  # 导入seaborn库
    data = pd.DataFrame({'A': [1, 2, 3], 'B': [3, 2, 1], 'C': [1, 2, 3]})  # 构造含正负相关的数据
    for cmap_name in ['coolwarm', 'viridis', 'Greens']:  # 依次切换配色方案
        fig, ax = plt.subplots(figsize=(4, 3.5))  # 新建画布
        sns.heatmap(data.corr(), annot=True, cmap=cmap_name, ax=ax)  # 不设center,直接出图
        fig.savefig('heatmap_' + cmap_name + '.png')  # 按配色名保存图形

    预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):代码无控制台输出、正常生成三张图;coolwarm 图中红蓝分居对角线两侧,viridis 图正负格子呈紫黄两端色,Greens 图整体绿色深浅、−1 与弱正值难以区分。

    回扣主线:发散型配色以 0 为锚编码正负相关的论述见本章”相关性热力图”一节,图形选型总则见第 章节 20 章。